package com.itheima.reggie.security;

import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.R;
import com.itheima.reggie.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * WebSecurityConfigurerAdapter是一个适配器，编写自定义的认证授权的配置类要继承于它
 */
@Slf4j
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private EmployeeDetailsServiceImpl employeeDetailsService;

    /**
     * BCryptPasswordEncoder 这个一个security的加密方法 它是一个不可逆的形式。
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bc(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 过滤器loginFilter写好了，要让它变为bean对象，然后交给springboot
     * @return
     * @throws Exception
     */
    @Bean
    public LoginFilter loginFilter() throws Exception{
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setFilterProcessesUrl("/employee/login");//这个是前端登录的地址
        loginFilter.setAuthenticationManager(authenticationManager());//设置自定义的AuthenticationManager认证管理器
        loginFilter.setAuthenticationSuccessHandler((request,response,authentication)->{
            //SecurityContextHolder系统安全的上下文，包括当前系统的用户信息。
            //应用从开启到结束只有一个用户在使用，整个应用只需要保存一个SecurityContext即可。
            //Authentication对象保存了这些用户信息。
            //principal是获取对象。
           // log.info("into loginFilter");
            Employee principal = (Employee) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            String string = JSON.toJSONString(R.success(principal));
           // log.info("string========"+string);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(string);
           // log.info("out loginFilter");
        });//是登录成功的结果处理
        loginFilter.setAuthenticationFailureHandler((request,response,authenticationException)->{
            String string = JSON.toJSONString(R.error("登录失败"));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(string);
        });//是登录失败的结果处理
        return loginFilter;
    }

    /**
     * 自定义一个AuthenticationManager认证管理器，生成bean对象，交给springboot管理。
     * @return
     */
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
          return super.authenticationManagerBean();
    }
    /**
     * AuthenticationManagerBuilder 是一个认证管理器的构建起 用来构建AuthenticationManager
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //数据库方式的认证,首先经过过滤器把信息交给认证管理器，然后自定义一个认证逻辑，里面有员工和角色的绑定。
        auth.userDetailsService(employeeDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //security默认配置formLogin来实现表单登录
        http.formLogin().loginPage("/backend/page/login/login.html").permitAll()
                .and().logout().logoutUrl("/logout")//当前session失效，重定向到登录页面 这个logout是security帮我们写的 不用调用自己的框架了
                .logoutSuccessHandler((request,response,authentication)->{
                    String string = JSON.toJSONString(R.error("退出登录成功"));
                    response.getWriter().write(string);
                }).and()
                .authorizeRequests()
                .antMatchers("/**/*.js","/**/*.css","/**/styles/**","/user/sendMsg","/**/*.ico","/**/images/**","/**/*.woff","/**/*.ttf","/doc.html",
                        "/webjars/**",
                        "/swagger-resources",
                        "/v2/api-docs")
                .permitAll()
                .anyRequest()
                .authenticated()  //表示除去以上放行的请求之外，其他请求都需要验证是否已经登录过才可以放行。
                .and().headers().frameOptions().sameOrigin()  //加入同源策略，页面加载时也会加载iframe子页面。
                .and().csrf().disable(); //关闭跨站请求伪造，不关闭登录不了。
         http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().accessDeniedHandler((request,response,accessDeniedException)->{
            String string = JSON.toJSONString(R.error("权限不足，禁止登录"));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(string);
        });
    }

}
